package main;

import java.util.LinkedList;

public class BranchUnit extends Main{
	
	static LinkedList<Instruction> inst = new LinkedList<Instruction>();
	static final int Cycles = 2;
	static boolean taken = false;
	static boolean stall = false;
	static int address;
	

	public static void execute() {
		
		if(inst.isEmpty()) return;
		
		Instruction i = inst.getFirst();
		
		if(!i.inExecution) {i.inExecution = true; return;}
		
		if(i.N == 0) {
			
			if(i.type.equals("BEQ")) taken = (i.v1 == i.v2);
			else taken = true;
			
		}
		
		i.N++;
		
		if(i.N == Cycles) {
			takeBranch(i);
			broadCastResult();
		} 
		
		
	}
	
	static void takeBranch(Instruction i) {
		
		if(!taken) return;
		
		if(i.type.equals("JMP")) address = 2*(i.turn+1)+i.v1+i.immediate;
		else if(i.type.equals("BEQ")) address = 2*(i.turn+1)+i.immediate;
		else address = i.v1;
		
		address/=2;
		
		MemoryUnit.finst.clear();
		MemoryUnit.beginFetch = false;
		MemoryUnit.ICycles = 0;
			
		PC = address;
		
		inst_buffer.clear();
		IBIndex = inst_buffer.size();
		stall = true;
		
		Fetch.stageInstructions.clear();
		Decode.stageInstructions.clear();
		Dispatch.stageInstructions.clear();
		
		int branchIndex = instructions.lastIndexOf(i);
		
		for(int j=branchIndex+1; j<instructions.size(); j++)
			instructions.get(j).valid = false;
		
		//Discard Instructions after the branch in Reservation Stations
		for(int j=0; j<ReservationSation.size(); j++){
			
			ResStationEntry e = ReservationSation.get(j);
			
			if(!e.inst.valid) {
				
				ReservationSation.remove(e);
				reserve--;
				j--;
			}
		}
		
		for(int j=0; j<instructions.size(); j++) {
			
			Instruction e = instructions.get(j);
			
			if(e.dest != -1 && !e.valid){
				RegRobMapping.remove(e.dest);
				usedPhysicalRegisters.remove(new Integer(e.dest));
				RegMapping.remove(e.destOriginal);
			}
			
		}
		
		// Discard Instructions after the branch in ROB
		for(int j=0; j<ROB.size(); j++){
			
			ROBEntry e = ROB.get(j);
			
			if(!e.inst.valid) {
				
				ROB.remove(e);
				reserveROB--;
				j--;
			}
		}
		
		//recover Register Mapping if any
		for(int j=0; j<ROB.size(); j++) {
			
			ROBEntry e = ROB.get(j);
			
			if(e.inst.dest !=-1 && e.inst.dest != e.inst.destOriginal) {
				RegMapping.put(e.inst.destOriginal, e.inst.dest);
			}
			
		}
		
		//Discard Instructions after the branch in all Units
		
		int Fsize = FS.size()+5;
		
		for(int k=0; k<Fsize; k++){
		
		LinkedList<Instruction> is;
		if(k == Fsize-1) is = MemoryUnit.inst;
		else if(k == Fsize-2) is = BranchUnit.inst;
		else if(k == Fsize-3) is = Execute.stageInstructions;
		else if(k == Fsize-4) is = instructions;
		else if(k == Fsize-5) is = Issue.stageInstructions;
		else is = FS.get(k).instructions;
		
		for(int j=0; j<is.size(); j++) {
			
			Instruction e = is.get(j);
			
				if(!e.valid) {
					is.remove(e);
					j--;
				}
			}
		}
		
	}
	
	static void broadCastResult() {
	
	Instruction ins = inst.removeFirst();
	
	if(!inst.isEmpty()) inst.getFirst().inExecution = true;
			
	ResStationEntry e2 = null;
	
	int physicalReg = (ins.dest);
	int robIndex = -1;
	if(physicalReg != -1) robIndex = RegRobMapping.get(ins.dest).value;
	
	int result = 0;
	
	if(ins.type.equals("JALR")) result = (ins.turn+1)*2;
			
	for(int i=0; i<ReservationSation.size(); i++) {
		
		ResStationEntry e = ReservationSation.get(i);
		if(e.inst == ins) e2 = e;		
		
		if(e.inst.source1 == physicalReg) {
			
			e.r1 = true;
			e.v1 = result;
			e.inst.v1 = result;
		}
		
		if(e.inst.source2 == physicalReg) {
			
			e.r2 = true;
			e.v2 = result;
			e.inst.v2 = result;
		}
		
		
	}
	
	//Write in ROB
		if(physicalReg != -1) {
		ROB.get(robIndex).value = result;
		}
		Execute.stageInstructions.add(ins);
		ReservationSation.remove(e2);
		reserve--;
			
	}

}
